package services;

import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;

import javax.transaction.Transactional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import repositories.AvailableItemRepository;
import domain.AvailableItem;
import domain.Comment;
import domain.Customer;
import domain.Money;
import domain.PurchasedItem;
import domain.ShoppingCartItemUnit;
import forms.ItemForm;

@Service
@Transactional
public class AvailableItemService {

	// Managed Repository
	@Autowired
	private AvailableItemRepository availableItemRepository;

	// Supporting Services
	@Autowired
	private CustomerService customerService;
	@Autowired
	private PurchasedItemService purchasedItemService;
	@Autowired
	private ShoppingCartItemUnitService shoppingCartItemUnitService;

	// Constructors
	public AvailableItemService() {
		super();
	}

	// Simple CRUD methods

	public AvailableItem create() {
		AvailableItem result;
		Customer seller;
		Collection<Customer> wishers;
		Collection<Comment> comments;
		Collection<ShoppingCartItemUnit> shoppingCartItemUnits;
		String referenceNumber;

		referenceNumber = UUID.randomUUID().toString();
		seller = customerService.findByPrincipal();

		result = new AvailableItem();

		wishers = new HashSet<Customer>();
		comments = new HashSet<Comment>();
		shoppingCartItemUnits = new HashSet<ShoppingCartItemUnit>();

		result.setSeller(seller);
		result.setReferenceNumber(referenceNumber);
		result.setWishers(wishers);
		result.setComments(comments);
		result.setShoppingCartItemUnits(shoppingCartItemUnits);

		return result;
	}

	public AvailableItem save(AvailableItem ai) {
		AvailableItem res;
		Assert.notNull(ai);

		Assert.isTrue(ai.getSeller().equals(customerService.findByPrincipal()));
		Assert.isTrue(ai.getState().equals("new")
				|| ai.getState().equals("used"));

		if (ai.getId() == 0) {
			Assert.isTrue(ai.getAvailableUnits() != 0);
		}

		res = availableItemRepository.save(ai);

		return res;
	}

	public AvailableItem saveUnits(AvailableItem ai) {
		AvailableItem res;
		Assert.notNull(ai);

		res = availableItemRepository.save(ai);

		return res;
	}

	public AvailableItem findOne(int availableItemId) {
		AvailableItem result;

		result = availableItemRepository.findOne(availableItemId);

		return result;
	}

	public Collection<AvailableItem> findAll() {
		Collection<AvailableItem> result;
		result = availableItemRepository.findAll();
		return result;
	}

	public void delete(AvailableItem ai) {
		Assert.notNull(ai);

		Collection<PurchasedItem> purchasedItems;
		Customer principal;

		principal = customerService.findByPrincipal();
		purchasedItems = purchasedItemService
				.findPurchasedItemIdByAvailableItemRefNumber(ai
						.getReferenceNumber());

		Assert.isTrue(ai.getSeller().equals(principal));
		Assert.isTrue(purchasedItems.isEmpty());

		if (!ai.getShoppingCartItemUnits().isEmpty()) {
			Collection<ShoppingCartItemUnit> shoppingCartItemUnits;

			shoppingCartItemUnits = ai.getShoppingCartItemUnits();
			for (ShoppingCartItemUnit cart : shoppingCartItemUnits) {
				if (cart.getAvailableItem().equals(ai)) {
					shoppingCartItemUnitService.deleteBecauseItemDelete(cart);
				}

			}
		}
		if (!ai.getWishers().isEmpty()) {
			for (Customer c : ai.getWishers()) {
				Collection<AvailableItem> wishesItems;
				wishesItems = c.getWishList();
				wishesItems.remove(ai);
				c.setWishList(wishesItems);
				customerService.save(c);
			}
		}

		availableItemRepository.delete(ai);

	}

	// Other Business methods

	public Collection<AvailableItem> findWishedItems() {
		Collection<AvailableItem> res;
		Customer customer;

		customer = customerService.findByPrincipal();
		res = availableItemRepository.findWishedItemsByCustomer(customer
				.getId());

		return res;
	}

	public AvailableItem addToWishlist(AvailableItem availableItem) {
		Assert.notNull(availableItem);
		AvailableItem res;
		Customer principal;
		Customer cUpdated;

		principal = customerService.findByPrincipal();
		principal.getWishList().add(availableItem);
		cUpdated = customerService.save(principal);

		availableItem.getWishers().add(cUpdated);
		res = availableItemRepository.save(availableItem);

		return res;
	}

	public AvailableItem removeFromWishlist(AvailableItem availableItem) {
		Assert.notNull(availableItem);
		AvailableItem res;
		Customer principal;
		Customer cUpdated;

		principal = customerService.findByPrincipal();
		principal.getWishList().remove(availableItem);
		cUpdated = customerService.save(principal);

		availableItem.getWishers().remove(cUpdated);
		res = availableItemRepository.save(availableItem);

		return res;
	}

	public void emptyWishList() {
		Customer customer;
		Collection<AvailableItem> wishlist;

		customer = customerService.findByPrincipal();
		wishlist = new HashSet<AvailableItem>();

		customer.setWishList(wishlist);

		customerService.save(customer);

	}

	public Collection<AvailableItem> findCartItems() {
		Collection<AvailableItem> res;
		Customer customer;

		customer = customerService.findByPrincipal();
		res = availableItemRepository.findCartItemsByCustomer(customer.getId());

		return res;
	}

	public void addToCart(AvailableItem availableItem) {
		Assert.notNull(availableItem);
		Assert.isNull(shoppingCartItemUnitService
				.findByCustomerAndItem(availableItem));
		Assert.isTrue(availableItem.getAvailableUnits() > 0);
		ShoppingCartItemUnit sciu;

		sciu = shoppingCartItemUnitService.create(availableItem);
		shoppingCartItemUnitService.save(sciu);
	}

	public void addAnotherUnit(AvailableItem availableItem) {
		Assert.notNull(availableItem);
		Assert.isTrue(availableItem.getAvailableUnits() > 0);
		ShoppingCartItemUnit sciu;

		sciu = shoppingCartItemUnitService.findByCustomerAndItem(availableItem);
		Assert.isTrue(availableItem.getAvailableUnits() > sciu
				.getNumberOfItems());

		sciu.setNumberOfItems(sciu.getNumberOfItems() + 1);
		shoppingCartItemUnitService.save(sciu);
	}

	public void removeFromCart(AvailableItem availableItem) {
		Assert.notNull(availableItem);
		ShoppingCartItemUnit sciu;
		ShoppingCartItemUnit updated;

		sciu = shoppingCartItemUnitService.findByCustomerAndItem(availableItem);
		sciu.setNumberOfItems(sciu.getNumberOfItems() - 1);
		updated = shoppingCartItemUnitService.save(sciu);

		if (sciu.getNumberOfItems() == 0) {
			shoppingCartItemUnitService.delete(updated);
		}
	}

	public void emptyCart() {
		Collection<ShoppingCartItemUnit> cart;

		cart = shoppingCartItemUnitService.findByCustomer();

		for (ShoppingCartItemUnit s : cart) {
			shoppingCartItemUnitService.delete(s);
		}

	}

	public Collection<AvailableItem> findAvailableItems() {
		Collection<AvailableItem> result;

		result = availableItemRepository.findAvailableItems();

		return result;
	}

	public Collection<AvailableItem> findAvailableItemsBySellerId() {
		Collection<AvailableItem> result;
		Customer seller;

		seller = customerService.findByPrincipal();

		result = availableItemRepository.findAvailableItemsBySellerId(seller
				.getId());

		return result;
	}

	public Collection<AvailableItem> findAvailableItemsByOthersCustomer() {
		Collection<AvailableItem> result;
		Customer principal;

		principal = customerService.findByPrincipal();

		result = availableItemRepository
				.findAvailableItemsByOthersCustomer(principal.getId());

		return result;
	}

	public int findAvailableItemIdByPurchasedItemRefNumber(String refNumber) {
		int result;
		Customer principal;
		principal = customerService.findByPrincipal();
		Assert.isTrue(principal instanceof Customer);

		result = availableItemRepository
				.findAvailableItemIdByPurchasedItemRefNumber(refNumber);
		return result;
	}

	public Collection<AvailableItem> findBestRatedBySeller() {

		Customer principal;
		principal = customerService.findByPrincipal();
		Assert.isTrue(principal instanceof Customer);

		Collection<Object[]> objects;
		AvailableItem availableItem;
		List<AvailableItem> res;
		Double ratingMax;
		List<Integer> idres;

		ratingMax = 0.0;
		res = new LinkedList<AvailableItem>();
		idres = new LinkedList<Integer>();
		objects = availableItemRepository.findBestRatedBySeller(principal
				.getId());

		for (Object[] o : objects) {
			if (o[0] == null) {
				continue;
			} else {
				int idAvailableItem = (int) o[0];
				Double rating = (Double) o[1];

				if (ratingMax < rating) {
					ratingMax = rating;
					idres.clear();
					idres.add(idAvailableItem);
				} else if (ratingMax == rating) {
					idres.add(idAvailableItem);
				}
			}
		}
		for (Integer l : idres) {
			availableItem = findOne(l);
			res.add(availableItem);

		}

		return res;

	}

	// Form Methods
	public ItemForm availableItemToForm(AvailableItem ai) {
		ItemForm res;
		Assert.notNull(ai);

		res = new ItemForm();

		res.setItemId(ai.getId());

		res.setReferenceNumber(ai.getReferenceNumber());
		res.setName(ai.getName());
		res.setDescription(ai.getDescription());
		res.setState(ai.getState());
		if (ai.getId() != 0) {
			res.setAmount(ai.getPrice().getAmount());
			res.setCurrency(ai.getPrice().getCurrency());
		}

		res.setAvailableUnits(ai.getAvailableUnits());

		res.setSeller(ai.getSeller());

		res.setPurchasedUnits(1);

		return res;
	}

	public AvailableItem reconstruct(ItemForm itf) {
		AvailableItem res;
		Money money;

		money = new Money();
		Assert.isTrue(itf.getState().equals("new")
				|| itf.getState().equals("used"));

		if (itf.getItemId() == 0) {
			res = create();

			res.setReferenceNumber(itf.getReferenceNumber());
			res.setName(itf.getName());
			res.setDescription(itf.getDescription());
			res.setState(itf.getState());

			money.setAmount(itf.getAmount());
			money.setCurrency(itf.getCurrency());
			res.setPrice(money);

			res.setAvailableUnits(itf.getAvailableUnits());

		} else {
			res = findOne(itf.getItemId());
			Assert.notNull(res);

			res.setReferenceNumber(itf.getReferenceNumber());
			res.setName(itf.getName());
			res.setDescription(itf.getDescription());
			res.setState(itf.getState());

			money.setAmount(itf.getAmount());
			money.setCurrency(itf.getCurrency());
			res.setPrice(money);

			res.setAvailableUnits(itf.getAvailableUnits());

		}

		return res;
	}

}
